package com.nervenets.general.utils;

import com.nervenets.general.exception.LogicException;
import com.nervenets.general.hibernate.DomainObject;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.http.HttpStatus;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public final class ClassUtils {
    public static <T> Field[] getAllDeclaredFields(Class<T> clazz) {
        List<Field[]> fieldArrayList = new ArrayList<>();
        while (clazz != null) {
            fieldArrayList.add(clazz.getDeclaredFields());
            clazz = (Class<T>) clazz.getSuperclass();
        }

        int fieldCount = 0;
        int fieldIndex = 0;
        for (Field[] fieldArray : fieldArrayList) {
            fieldCount = fieldCount + fieldArray.length;
        }

        Field[] allFields = new Field[fieldCount];
        for (Field[] fieldArray : fieldArrayList) {
            for (Field field : fieldArray) {
                allFields[fieldIndex++] = field;
            }
        }

        return allFields;
    }

    public static Field getDeclaredField(Class<?> source, String[] fields) {
        Field field = null;
        Class<?> s = source;
        for (String f : fields) {
            field = getDeclaredField(s, f);
            s = field.getType();
        }
        return field;
    }

    public static Field getDeclaredField(Class<?> source, String field) {
        return ReflectionUtils.findField(source, field);
    }

    public static List<Field> getDeclaredAnnotationField(Class<?> source, Class<? extends Annotation> type) {
        return Arrays.stream(getAllDeclaredFields(source)).filter(field -> field.isAnnotationPresent(type)).collect(Collectors.toList());
    }

    public static Constructor<?> getDeclaredConstructor(Class<?> source, Class<?> arg) {
        Constructor<?> constructor = null;
        try {
            constructor = source.getDeclaredConstructor(arg);
        } catch (NoSuchMethodException ignored) {
        }

        if (null == constructor) {
            Class<?>[] interfaces = arg.getInterfaces();
            if (interfaces.length > 0) {
                for (Class<?> face : interfaces) {
                    try {
                        constructor = source.getDeclaredConstructor(face);
                    } catch (NoSuchMethodException ignored) {

                    }
                    if (null != constructor) break;
                }
            }
        }

        if (null == constructor) {
            Class<?> superclass = arg.getSuperclass();
            while (null != superclass) {
                try {
                    constructor = source.getDeclaredConstructor(superclass);
                } catch (NoSuchMethodException ignored) {

                }
                if (null != constructor) break;
                superclass = superclass.getSuperclass();
            }
        }

        if (null == constructor)
            throw new LogicException(HttpStatus.BAD_REQUEST.value(), "在对象【%s】中未找到参数为【%s】的构造方法", source, arg);

        return constructor;
    }

    public static Object callMethod(Object o, String methodName, Object... args) {
        try {
            Method method = o.getClass().getMethod(methodName);
            return method.invoke(o, args);
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new LogicException(String.format("调用%s的方法%s失败", o.getClass().getSimpleName(), methodName));
    }

    public static String getterMethod(String propertyName) {
        return "get" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
    }

    public static String setterMethod(String propertyName) {
        return "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
    }

    public static boolean isInherited(Class child, Class parent) {
        for (Class type = child; type != null; type = type.getSuperclass()) {
            if (type.equals(parent)) {
                return true;
            }
        }
        return false;
    }

    public static String getClassRealName(String classPath) {
        return classPath.substring(classPath.lastIndexOf(".") + 1);
    }

    public static <T extends Annotation> T getFieldAnnotation(Class<?> clz, String filedName, Class<T> type) {
        final Field declaredField = ClassUtils.getDeclaredField(clz, filedName);
        return declaredField.getAnnotation(type);
    }

    public static void trimAllStringValue(Object obj) throws InvocationTargetException, IllegalAccessException {
        BeanWrapper wrappedSource = new BeanWrapperImpl(obj);
        for (PropertyDescriptor descriptor : wrappedSource.getPropertyDescriptors()) {
            Method readMethod = descriptor.getReadMethod();
            Method writeMethod = descriptor.getWriteMethod();
            if (readMethod != null && writeMethod != null && descriptor.getPropertyType() == String.class) {
                String value = (String) readMethod.invoke(obj);
                if (value != null) writeMethod.invoke(obj, value.trim());
            }
        }
    }
}
